home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sprite 1984 - 1993
/
Sprite 1984 - 1993.iso
/
src
/
boot
/
netBoot.new
/
dev
/
si.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-12-19
|
15KB
|
674 lines
#ifndef lint
/* static char sccsid[] = "@(#)si.c 1.1 86/09/27 Copyr 1986 Sun Micro"; */
#endif
/*
* Copyright (c) 1986 by Sun Microsystems, Inc.
*/
#ifdef SUN3
#include "../h/types.h"
#include "../h/buf.h"
#include "../dev/dklabel.h"
#include "../dev/dkio.h"
#include "../dev/screg.h"
#include "../dev/sireg.h"
#include "../dev/scsi.h"
#include "../dev/saio.h"
#include "../h/sunromvec.h"
#include "../h/idprom.h"
/*
* Low-level routines common to all devices on the SCSI bus.
*/
/*
* Interface to the routines in this module is via a second "sip"
* structure contained in the caller's local variables.
*
* This "sip" must be initialized with sip->si_boottab = sidriver
* and must then be devopen()ed before any I/O can be done.
*
* The device must be closed with devclose().
*/
/* how si addresses look to the si vme scsi dma hardware */
#define SI_VME_DMA_ADDR(x) (((int)x)&0x000FFFFF)
/* how si addresses look to the sun3/50 scsi dma hardware */
#define SI_OB_DMA_ADDR(x) (((int)x)&0x00FFFFFF)
struct sidma {
struct udc_table udct; /* dma information for udc */
};
/*
* The interfaces we export
*/
extern char *devalloc();
extern char *resalloc();
extern int nullsys();
int siopen();
struct boottab sidriver =
{"sd", nullsys, nullsys, siopen, nullsys,
nullsys, "", 0};
#define SI_VME_BASE 0x200000
#define SI_OB_BASE 0x140000
#define SI_SIZE 0x4000
/*
* Probe for si host adaptor interface.
* Return 1 if found one, 0 otherwise.
*/
int
siprobe(sip)
register struct saioreq *sip;
{
register struct scsi_si_reg *sir;
struct idprom id;
register int base;
register int scsi_nstd;
register int ctlr;
enum MAPTYPES space;
#ifdef M25
/* determine type of si interface */
if ((idprom(IDFORM_1, &id) == IDFORM_1) &&
(id.id_machine == IDM_SUN3_M25)) {
return (1);
} else {
return (0);
}
#else
/* determine type of si interface */
if (idprom(IDFORM_1, &id) == IDFORM_1) {
base = SI_VME_BASE;
space = MAP_VME24A16D;
scsi_nstd = 2;
} else {
return (0);
}
/* get base address of registers */
if (sip->si_ctlr < scsi_nstd) {
ctlr = base + (sip->si_ctlr * SI_SIZE);
} else {
return (0);
}
/* now map it in */
if ((sir = (struct scsi_si_reg *)devalloc(space, ctlr,
sizeof(struct scsi_si_reg))) == 0) {
return (0);
}
/*
* SI vme scsi host adaptor occupies 2K bytes in the vme
* address space. SC vme scsi host adaptor occupies 4K
* bytes in the vme address space.
* peek past 2K bytes to determine which host adaptor is there.
*/
if (peek((int)sir+0x800) == -1) {
return (1);
} else {
return (0);
}
#endif M25
}
/*
* Open the SCSI host adapter.
*/
int
siopen(sip)
register struct saioreq *sip;
{
register struct scsi_si_reg *sir;
struct idprom id;
register int base;
register int scsi_nstd;
enum MAPTYPES space;
/* determine type of si interface */
if (idprom(IDFORM_1, &id) == IDFORM_1) {
#ifdef M25
if (id.id_machine == IDM_SUN3_M25) {
base = SI_OB_BASE;
space = MAP_OBIO;
scsi_nstd = 1;
} else {
return (-1);
}
#else
base = SI_VME_BASE;
space = MAP_VME24A16D;
scsi_nstd = 2;
#endif M25
} else {
return (-1);
}
/* get base address of registers */
if (sip->si_ctlr < scsi_nstd) {
sip->si_ctlr = base + (sip->si_ctlr * SI_SIZE);
} else {
return (-1);
}
/* now map it in */
sip->si_devaddr = devalloc(space, sip->si_ctlr,
sizeof(struct scsi_si_reg));
if (sip->si_devaddr == 0) {
return (-1);
}
/* allocate dma resources */
#ifdef M25
sip->si_dmaaddr = resalloc(RES_DMAMEM, sizeof(struct sidma));
if (sip->si_dmaaddr == 0) {
return (-1);
}
#else
sip->si_dmaaddr = 0;
#endif M25
/* reset registers */
sir = (struct scsi_si_reg *) sip->si_devaddr;
sir->csr = 0;
DELAY(10);
sir->csr = SI_CSR_SCSI_RES | SI_CSR_FIFO_RES;
sir->bcr = 0;
#ifndef M25
sir->dma_addr = 0;
sir->dma_count = 0;
sir->iv_am = VME_SUPV_DATA_24;
#endif
return (0);
}
/*
* Write a command to the SCSI bus.
*
* The supplied sip is the one opened by siopen().
* DMA is done based on sip->si_ma and sip->si_cc.
*
* Returns -1 for error, otherwise returns the residual count not DMAed
* (zero for success).
*
* FIXME, this must be accessed via a boottab vector,
* to allow host adap to switch.
* Must pass cdb, scb in sip somewhere...
*/
int
sidoit(cdb, scb, sip)
struct scsi_cdb *cdb;
struct scsi_scb *scb;
register struct saioreq *sip;
{
register struct scsi_si_reg *sir;
register char *cp;
register int i;
register int b;
register int ob;
u_char junk;
/* get to scsi control logic registers */
sir = (struct scsi_si_reg *) sip->si_devaddr;
#ifdef M25
ob = 1;
#else
ob = 0;
#endif
i = 100000;
for(;;) {
if ((sir->sbc_rreg.cbsr & SBC_CBSR_BSY) == 0) {
break;
}
if (i-- <= 0) {
si_reset(sir, ob);
return (-1);
}
}
/* select target */
sir->sbc_wreg.odr = (1 << sip->si_unit) | SI_HOST_ID;
sir->sbc_wreg.icr = SBC_ICR_DATA;
sir->sbc_wreg.icr |= SBC_ICR_SEL;
/* wait for target to acknowledge our selection */
if (si_sbc_wait(&sir->sbc_rreg.cbsr, SBC_CBSR_BSY, 1) == 0) {
sir->sbc_wreg.icr = 0;
sir->sbc_wreg.odr = 0;
return (-1);
}
sir->sbc_wreg.icr = 0;
/* do initial dma setup */
sir->bcr = 0; /* also reset dma_count for vme */
if (sip->si_cc > 0) {
if ((cdb->cmd == SC_READ) || (cdb->cmd == SC_REQUEST_SENSE)) {
sir->csr &= ~SI_CSR_SEND;
} else {
sir->csr |= SI_CSR_SEND;
}
sir->csr &= ~SI_CSR_FIFO_RES;
sir->csr |= SI_CSR_FIFO_RES;
sir->bcr = sip->si_cc;
#ifndef M25
sir->bcrh = 0;
#endif
}
/* put command onto scsi bus */
cp = (char *)cdb;
if (si_putbyte(sir, PHASE_COMMAND, cp, sizeof(struct scsi_cdb)) == 0) {
sc_error("put of cmd onto scsi bus failed");
goto failed;
}
/* finish dma setup and wait for dma completion */
if (sip->si_cc > 0) {
#ifdef M25
si_ob_dma_setup(sir,
&(((struct sidma *)sip->si_dmaaddr)->udct), cdb->cmd,
sip->si_cc, sip->si_ma);
#else
if ((int)sip->si_ma & 1) {
sc_error("dma begins on odd address");
goto failed;
} else if ((int)sip->si_ma & 2) {
sir->csr |= SI_CSR_BPCON;
} else {
sir->csr &= ~SI_CSR_BPCON;
}
sir->dma_addr = SI_VME_DMA_ADDR(sip->si_ma);
sir->dma_count = sip->si_cc;
#endif M25
/* setup sbc and start dma */
sir->sbc_wreg.mr |= SBC_MR_DMA;
if ((cdb->cmd == SC_READ) || (cdb->cmd == SC_REQUEST_SENSE)) {
sir->sbc_wreg.tcr = TCR_DATA_IN;
sir->sbc_wreg.ircv = 0;
} else {
sir->sbc_wreg.tcr = TCR_DATA_OUT;
sir->sbc_wreg.icr = SBC_ICR_DATA;
sir->sbc_wreg.send = 0;
}
#ifndef M25
sir->csr |= SI_CSR_DMA_EN;
#endif M25
/* wait for dma completion */
if (si_wait(&sir->csr,
SI_CSR_SBC_IP|SI_CSR_DMA_IP|SI_CSR_DMA_CONFLICT, 1)
== 0) {
sc_error("dma never completed");
if (ob == 0) {
sir->csr &= ~SI_CSR_DMA_EN;
}
si_dma_cleanup(sir, ob);
goto failed;
}
#ifndef M25
sir->csr &= ~SI_CSR_DMA_EN;
#endif M25
/* check reason for dma completion */
if (sir->csr & SI_CSR_SBC_IP) {
/* dma operation should end with a phase mismatch */
si_sbc_wait(&sir->sbc_rreg.bsr, SBC_BSR_PMTCH, 0);
} else {
if (sir->csr & SI_CSR_DMA_CONFLICT) {
sc_error("invalid reg access during dma");
} else if (sir->csr & SI_CSR_DMA_BUS_ERR) {
sc_error("bus error during dma");
} else {
#ifdef M25
sc_error("unknown dma failure");
#else
sc_error("dma overrun");
#endif M25
}
si_dma_cleanup(sir, ob);
goto failed;
}
/* handle special dma recv situations */
if ((cdb->cmd == SC_READ) || (cdb->cmd == SC_REQUEST_SENSE)) {
#ifdef M25
sir->udc_raddr = UDC_ADR_COUNT;
if (si_wait(&sir->csr, SI_CSR_FIFO_EMPTY, 1) == 0) {
sc_error("fifo never emptied");
si_dma_cleanup(sir, ob);
goto failed;
}
/* if odd byte recv, must grab last byte by hand */
if ((sip->si_cc - sir->bcr) & 1) {
cp = sip->si_ma + (sip->si_cc - sir->bcr) - 1;
*cp = (sir->fifo_data & 0xff00) >> 8;
/* udc may not dma last word */
} else if (((sir->udc_rdata*2) - sir->bcr) == 2) {
cp = sip->si_ma + (sip->si_cc - sir->bcr);
*(cp - 2) = (sir->fifo_data & 0xff00) >> 8;
*(cp - 1) = sir->fifo_data & 0x00ff;
}
#else
if ((sir->csr & SI_CSR_LOB) != 0) {
cp = sip->si_ma + (sip->si_cc - sir->bcr);
if ((sir->csr & SI_CSR_BPCON) == 0) {
switch (sir->csr & SI_CSR_LOB) {
case SI_CSR_LOB_THREE:
*(cp - 3) = (sir->bpr & 0xff000000) >> 24;
*(cp - 2) = (sir->bpr & 0x00ff0000) >> 16;
*(cp - 1) = (sir->bpr & 0x0000ff00) >> 8;
break;
case SI_CSR_LOB_TWO:
*(cp - 2) = (sir->bpr & 0xff000000) >> 24;
*(cp - 1) = (sir->bpr & 0x00ff0000) >> 16;
break;
case SI_CSR_LOB_ONE:
*(cp - 1) = (sir->bpr & 0xff000000) >> 24;
break;
}
} else {
*(cp - 1) = (sir->bpr & 0x0000ff00) >> 8;
}
}
#endif M25
}
/* clear sbc interrupt */
junk = sir->sbc_rreg.clr;
/* cleanup after a dma operation */
si_dma_cleanup(sir, ob);
}
/* get status */
cp = (char *)scb;
for (i = 0;;) {
b = si_getbyte(sir, PHASE_STATUS);
if (b == -1) {
break;
}
if (i < STATUS_LEN) {
cp[i++] = b;
}
}
b = si_getbyte(sir, PHASE_MSG_IN);
if (b != SC_COMMAND_COMPLETE) {
if (b >= 0) { /* if not, si_getbyte already printed msg */
sc_error("invalid message");
}
goto failed;
}
return (sip->si_cc - sir->bcr);
failed:
si_reset(sir, ob);
return (-1);
}
#ifdef M25
si_ob_dma_setup(sir, udct, cmd, cc, ma)
register struct scsi_si_reg *sir;
register struct udc_table *udct;
register u_char cmd;
register int cc;
register char *ma;
{
/* setup udc dma info */
udct->haddr = ((SI_OB_DMA_ADDR(ma) & 0xff0000) >> 8) |
UDC_ADDR_INFO;
udct->laddr = SI_OB_DMA_ADDR(ma) & 0xffff;
udct->hcmr = UDC_CMR_HIGH;
udct->count = cc / 2;
if ((cmd == SC_READ) || (cmd == SC_REQUEST_SENSE)) {
udct->rsel = UDC_RSEL_RECV;
udct->lcmr = UDC_CMR_LRECV;
} else {
udct->rsel = UDC_RSEL_SEND;
udct->lcmr = UDC_CMR_LSEND;
if (cc & 1) {
udct->count++;
}
}
/* initialize chain address register */
DELAY(SI_UDC_WAIT);
sir->udc_raddr = UDC_ADR_CAR_HIGH;
DELAY(SI_UDC_WAIT);
sir->udc_rdata = ((int)udct & 0xff0000) >> 8;
DELAY(SI_UDC_WAIT);
sir->udc_raddr = UDC_ADR_CAR_LOW;
DELAY(SI_UDC_WAIT);
sir->udc_rdata = (int)udct & 0xffff;
/* initialize master mode register */
DELAY(SI_UDC_WAIT);
sir->udc_raddr = UDC_ADR_MODE;
DELAY(SI_UDC_WAIT);
sir->udc_rdata = UDC_MODE;
/* issue start chain command */
DELAY(SI_UDC_WAIT);
sir->udc_raddr = UDC_ADR_COMMAND;
DELAY(SI_UDC_WAIT);
sir->udc_rdata = UDC_CMD_STRT_CHN;
}
#endif M25
/*
* Reset some register information after a dma operation.
*/
si_dma_cleanup(sir, ob)
register struct scsi_si_reg *sir;
register int ob;
{
#ifdef M25
sir->udc_raddr = UDC_ADR_COMMAND;
DELAY(SI_UDC_WAIT);
sir->udc_rdata = UDC_CMD_RESET;
#else
sir->csr &- ~SI_CSR_DMA_EN;
sir->dma_addr = 0;
#endif M25
sir->sbc_wreg.mr &= ~SBC_MR_DMA;
sir->sbc_wreg.icr = 0;
sir->sbc_wreg.tcr = 0;
}
/*
* Wait for a condition to be (de)asserted.
*/
si_wait(reg, cond, set)
register u_short *reg;
register u_short cond;
register int set;
{
register int i;
register u_short regval;
for (i = 0; i < 3000; i++) {
regval = *reg;
if ((set == 1) && (regval & cond)) {
return (1);
}
if ((set == 0) && !(regval & cond)) {
return (1);
}
DELAY(5000);
}
return (0);
}
/*
* Wait for a condition to be (de)asserted on the scsi bus.
*/
si_sbc_wait(reg, cond, set)
register caddr_t reg;
register u_char cond;
register int set;
{
register int i;
register u_char regval;
for (i = 0; i < 1000; i++) {
regval = *reg;
if ((set == 1) && (regval & cond)) {
return (1);
}
if ((set == 0) && !(regval & cond)) {
return (1);
}
DELAY(5000);
}
return (0);
}
/*
* Put a byte onto the scsi bus.
*/
si_putbyte(sir, phase, data, numbytes)
register struct scsi_si_reg *sir;
register u_short phase;
register u_char *data;
register int numbytes;
{
register int i;
/* set up tcr so a phase match will occur */
if (phase == PHASE_COMMAND) {
sir->sbc_wreg.tcr = TCR_COMMAND;
} else if (phase == PHASE_MSG_OUT) {
sir->sbc_wreg.tcr = TCR_MSG_OUT;
} else {
sc_error("putbyte, bad phase specified");
return (0);
}
/* put all desired bytes onto scsi bus */
for (i = 0; i < numbytes; i++) {
/* wait for target to request a byte */
if (si_sbc_wait(&sir->sbc_rreg.cbsr, SBC_CBSR_REQ, 1) == 0) {
sc_error("putbyte: target never set REQ");
return (0);
}
/* load data for transfer */
sir->sbc_wreg.odr = *data++;
sir->sbc_wreg.icr = SBC_ICR_DATA;
/* make sure phase match occurred */
if ((sir->sbc_rreg.bsr & SBC_BSR_PMTCH) == 0) {
sc_error("putbyte: phase mismatch");
return (0);
}
/* complete req/ack handshake */
sir->sbc_wreg.icr |= SBC_ICR_ACK;
if (si_sbc_wait(&sir->sbc_rreg.cbsr, SBC_CBSR_REQ, 0) == 0) {
sc_error("putbyte: target never released REQ");
return (0);
}
sir->sbc_wreg.icr = 0;
}
sir->sbc_wreg.tcr = 0;
return (1);
}
/*
* Get a byte from the scsi bus.
*/
si_getbyte(sir, phase)
register struct scsi_si_reg *sir;
register u_short phase;
{
register u_char data;
register int i;
/* set up tcr so a phase match will occur */
if (phase == PHASE_STATUS) {
sir->sbc_wreg.tcr = TCR_STATUS;
} else if (phase == PHASE_MSG_IN) {
sir->sbc_wreg.tcr = TCR_MSG_IN;
} else {
sc_error("getbyte, bad phase specified");
return (-1);
}
/* wait for target request */
for (i=0; i < 20; i++) {
if (si_sbc_wait(&sir->sbc_rreg.cbsr, SBC_CBSR_REQ, 1)) {
break;
}
}
if (si_sbc_wait(&sir->sbc_rreg.cbsr, SBC_CBSR_REQ, 1) == 0) {
sc_error("getbyte: target never set REQ");
sir->sbc_wreg.tcr = 0;
return (-1);
}
/* check for correct information phase on scsi bus */
if (phase != (sir->sbc_rreg.cbsr & CBSR_PHASE_BITS)) {
if (phase != PHASE_STATUS) {
sc_error("getbyte: phase mismatch");
}
sir->sbc_wreg.tcr = 0;
return (-1);
}
/* grab data */
data = sir->sbc_rreg.cdr;
sir->sbc_wreg.icr = SBC_ICR_ACK;
/* complete req/ack handshake */
if (si_sbc_wait(&sir->sbc_rreg.cbsr, SBC_CBSR_REQ, 0) == 0) {
sc_error("getbyte: target never released REQ");
sir->sbc_wreg.icr = 0;
sir->sbc_wreg.tcr = 0;
return (-1);
}
sir->sbc_wreg.icr = 0;
sir->sbc_wreg.tcr = 0;
return (data);
}
/*
* Reset SCSI control logic.
*/
si_reset(sir, ob)
register struct scsi_si_reg *sir;
register int ob;
{
register u_char junk;
/* reset bcr, fifo, udc, and sbc */
sir->bcr = 0;
sir->csr = 0;
DELAY(10);
sir->csr = SI_CSR_SCSI_RES|SI_CSR_FIFO_RES;
#ifndef M25
sir->dma_addr = 0;
sir->dma_count = 0;
#endif M25
/* issue scsi bus reset */
sir->sbc_wreg.icr = SBC_ICR_RST;
DELAY(50);
sir->sbc_wreg.icr = 0;
junk = sir->sbc_rreg.clr;
DELAY(10000000);
}
#endif SUN3